#pragma once

#include<iostream>
#include<assert.h>
#include<utility>

using namespace std;

	enum Colour
	{
		RED,
		BLACK
	};
	template< class T>
	class RBTreeNode
	{
	public:
		RBTreeNode<T>* _left;
		RBTreeNode<T>* _right;
		RBTreeNode<T>* _parent;
		T _data;
		Colour _col;

		RBTreeNode(const T& data)
			:_left(nullptr), _right(nullptr), _parent(nullptr), _col(RED), _data(data)
		{}
	};
	 
	template<class T,class Ptr,class Ref>
	struct TreeIterator
	{
		typedef RBTreeNode<T> Node;
		typedef TreeIterator<T,Ptr,Ref> self;
		typedef TreeIterator<T, T*, T&> Iterator;
		Node* _node;

		TreeIterator(Node* node)
			:_node(node)
		{}
		TreeIterator(const Iterator& it)
			:_node(it._node)
		{}

		Ref operator* ()
		{
			return _node->_data;
		}

		Ptr operator->()
		{
			return &_node->_data;
		}
		
		bool operator != (const self & s) const 
		{
			return _node!= s._node;
		}
		bool operator== (const self & s) const 
		{
			return _node == s._node;
		}
		self& operator--()
		{
			if (_node->_left)
			{
				Node* cur = _node->_right;
				while (cur->_left)
				{
					cur = cur->_right;
				}
				_node = cur;
			}
			else
			{
				Node* cur = _node;
				Node* parent = _node->_parent;
				while (parent && parent->_left)
				{
					cur = cur->_parent;
					parent = parent->_parent;
				}

				_node = parent;
			}
			return *this;
		}
		
		self& operator++()
		{
			if (_node->_right)
			{
				Node* cur = _node->_right;
				while (cur->_left)
				{
					cur = cur->_left;
				}
				_node = cur;
			}
			else
			{
				Node* cur = _node;
				Node* parent = _node->_parent;
				while (parent)
				{
					if (cur == parent->_left)
					{
						break;
					}
					else
					{
						cur = cur->_parent;
						parent = parent->_parent;
					}

				}

				_node = parent;
			}
			return *this;
		}
	};

	template<class K, class T,class keyofT>
	class RBTree
	{
		typedef RBTreeNode<T> Node;

	public:
		typedef TreeIterator<T, T*, T&> iterator;

		typedef TreeIterator<T,const T*,const T&> const_iterator;

		iterator begin()
		{
			Node* leftmin = _root;
			while (leftmin->_left)
			{
				leftmin = leftmin->_left;
			}
			return iterator(leftmin);
		}

		iterator end()
		{
			return iterator(nullptr);
		}

		const_iterator begin()const 
		{
			Node* leftmin = _root;
			while (leftmin->_left)
			{
				leftmin = leftmin->_left;
			}
			return const_iterator(leftmin);
		}

		const_iterator end()const 
		{
			return const_iterator(nullptr);
		}


		Node* find(const T& data)
		{
			keyofT kot;
			Node* cur = _root;
			while (cur)
			{
				if (kot(data) > kot(cur->_data))
				{
					cur = cur->_right;
				}
				else if (kot(data) < kot(cur->_data))
				{
					cur = cur->_left;
				}
				else
				{
					return cur;
				}
			}

			return nullptr;
		}

		pair<iterator,bool> insert(const T& data)
		{
			if (_root == nullptr)
			{
				_root = new Node(data);
				_root->_col = BLACK;
				return make_pair(iterator(_root),true);
			}
			Node* parent = nullptr;
			Node* cur = _root;

			keyofT kot;

			while (cur)
			{
				if (kot(data) > kot(cur->_data))
				{
					parent = cur;
					cur = cur->_right;
				}
				else if (kot(data) < kot(cur->_data))
				{
					parent = cur;
					cur = cur->_left;
				}
				else
				{
					return make_pair(iterator(cur), false);
				}
			}

			cur = new Node(data);
			Node* newnode = cur;
			cur->_col = RED;

			if (kot(data) < kot(parent->_data))
			{
				parent->_left = cur;
			}
			else
			{
				parent->_right = cur;
			}
			cur->_parent = parent;

			while (parent && parent->_col == RED)
			{
				Node* grandparent = parent->_parent;
				if (parent == grandparent->_left)
				{
					Node* uncle = grandparent->_right;
					if (uncle && uncle->_col == RED)
					{
						parent->_col = uncle->_col = BLACK;
						grandparent->_col = RED;
						cur = grandparent;
						parent = cur->_parent;
					}
					else//uncle is not or black
					{
						if (cur == parent->_left)
						{
							RotateR(grandparent);
							grandparent->_col = RED;
							parent->_col = BLACK;
						}
						else
						{
							RotateL(parent);
							RotateR(grandparent);
							grandparent->_col = RED;
							cur->_col = BLACK;
						}
						break;
					}
				}
				else//parent == grandparent->_right
				{
					Node* uncle = grandparent->_left;

					if (uncle && uncle->_col == RED)
					{
						grandparent->_col = RED;
						parent->_col = uncle->_col = BLACK;
						cur = grandparent;
						parent = cur->_parent;
					}
					else
					{
						if (cur == parent->_right)
						{
							RotateL(grandparent);
							parent->_col = BLACK;
							grandparent->_col = RED;
						}
						else
						{
							RotateR(parent);
							RotateL(grandparent);
							cur->_col = BLACK;
							grandparent->_col = RED;
						}
					}
					break;
				}
			}
			_root->_col = BLACK;
			return make_pair(iterator(newnode),true );
		}

		void RotateR(Node* parent)
		{
			Node* cur = parent->_left;
			Node* curright = cur->_right;

			parent->_left = curright;

			if (curright)
			{
				curright->_parent = parent;
			}

			cur->_right = parent;

			Node* ppnode = parent->_parent;

			parent->_parent = cur;

			if (ppnode == nullptr)
			{
				_root = cur;
				cur->_parent = nullptr;
			}
			else
			{
				if (ppnode->_left == parent)
				{
					ppnode->_left = cur;
				}
				else
				{
					ppnode->_right = cur;
				}

				cur->_parent = ppnode;
			}

		}
		void RotateL(Node* parent)
		{

			Node* cur = parent->_right;

			Node* curleft = cur->_left;

			parent->_right = curleft;

			if (curleft)
			{
				curleft->_parent = parent;
			}

			cur->_left = parent;

			Node* ppnode = parent->_parent;

			parent->_parent = cur;

			if (parent == _root)
			{
				_root = cur;
				cur->_parent = nullptr;
			}
			else
			{
				if (ppnode->_left == parent)
				{
					ppnode->_left = cur;
				}
				else
				{
					ppnode->_right = cur;
				}

				cur->_parent = ppnode;
			}
		}


		bool is_balance()
		{
			return is_balance(_root);
		}

		bool Checknum(Node* root, int blacknum, int stdnum)
		{
			if (root == nullptr)
			{
				if (blacknum != stdnum)
				{
					return false;
				}
				return true;
			}

			if (root->_col == BLACK)
			{
				++blacknum;
			}

			return Checknum(root->_left, blacknum, stdnum)
				&& Checknum(root->_right, blacknum, stdnum);
		}
		bool is_balance(Node* root)
		{
			if (root == nullptr)
			{
				return true;
			}

			if (root->_col == RED)
			{
				return false;
			}
			int stdnum = 0;
			Node* cur = root;
			while (cur)
			{
				if (cur->_col == BLACK)
				{
					++stdnum;
				}
				cur = cur->_left;
			}

			return Checknum(root, 0, stdnum);
		}

	private:
		Node* _root = nullptr;
	};

